/**
 ******************************************************************************
 * @file           : bsp_gpio.c
 * @brief          : GPIO Driver Implementation (STM32F4xx)
 ******************************************************************************
 * @attention
 *
 * GPIO驱动程序实现文件
 * 提供简化的GPIO操作接口
 * 适配STM32F4xx系列
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/GPIO/bsp_gpio.h"
#include "./BSP/GPIO/bsp_gpio_config.h"

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
static void gpio_enable_clock(GPIO_TypeDef *GPIOx);
static uint32_t gpio_get_hal_mode(GPIO_Mode_e mode);
static uint32_t gpio_get_hal_pull(GPIO_Pull_e pull);
static uint32_t gpio_get_hal_speed(GPIO_Speed_e speed);

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       使能GPIO端口时钟
 *
 * @param[in]   GPIOx GPIO端口
 *
 * @retval      无
 *
 * @note        自动识别端口并使能对应时钟
 */
static void gpio_enable_clock(GPIO_TypeDef *GPIOx)
{
    if (GPIOx == GPIOA)
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();
    }
    else if (GPIOx == GPIOB)
    {
        __HAL_RCC_GPIOB_CLK_ENABLE();
    }
    else if (GPIOx == GPIOC)
    {
        __HAL_RCC_GPIOC_CLK_ENABLE();
    }
    else if (GPIOx == GPIOD)
    {
        __HAL_RCC_GPIOD_CLK_ENABLE();
    }
    else if (GPIOx == GPIOE)
    {
        __HAL_RCC_GPIOE_CLK_ENABLE();
    }
    else if (GPIOx == GPIOF)
    {
        __HAL_RCC_GPIOF_CLK_ENABLE();
    }
    else if (GPIOx == GPIOG)
    {
        __HAL_RCC_GPIOG_CLK_ENABLE();
    }
    else if (GPIOx == GPIOH)
    {
        __HAL_RCC_GPIOH_CLK_ENABLE();
    }
#ifdef GPIOI
    else if (GPIOx == GPIOI)
    {
        __HAL_RCC_GPIOI_CLK_ENABLE();
    }
#endif
}

/**
 * @brief       转换GPIO模式枚举到HAL库定义
 *
 * @param[in]   mode 自定义模式枚举
 *
 * @retval      HAL库模式定义
 */
static uint32_t gpio_get_hal_mode(GPIO_Mode_e mode)
{
    switch (mode)
    {
        case GPIO_MODE_INPUT:       return GPIO_MODE_INPUT;
        case GPIO_MODE_OUTPUT_PP:   return GPIO_MODE_OUTPUT_PP;
        case GPIO_MODE_OUTPUT_OD:   return GPIO_MODE_OUTPUT_OD;
        case GPIO_MODE_AF_PP:       return GPIO_MODE_AF_PP;
        case GPIO_MODE_AF_OD:       return GPIO_MODE_AF_OD;
        case GPIO_MODE_ANALOG:      return GPIO_MODE_ANALOG;
        default:                    return GPIO_MODE_INPUT;
    }
}

/**
 * @brief       转换GPIO上下拉枚举到HAL库定义
 *
 * @param[in]   pull 自定义上下拉枚举
 *
 * @retval      HAL库上下拉定义
 */
static uint32_t gpio_get_hal_pull(GPIO_Pull_e pull)
{
    switch (pull)
    {
        case GPIO_NOPULL:       return GPIO_NOPULL;
        case GPIO_PULLUP:       return GPIO_PULLUP;
        case GPIO_PULLDOWN:     return GPIO_PULLDOWN;
        default:                return GPIO_NOPULL;
    }
}

/**
 * @brief       转换GPIO速度枚举到HAL库定义
 *
 * @param[in]   speed 自定义速度枚举
 *
 * @retval      HAL库速度定义
 */
static uint32_t gpio_get_hal_speed(GPIO_Speed_e speed)
{
    switch (speed)
    {
        case GPIO_SPEED_LOW:        return GPIO_SPEED_FREQ_LOW;
        case GPIO_SPEED_MEDIUM:     return GPIO_SPEED_FREQ_MEDIUM;
        case GPIO_SPEED_HIGH:       return GPIO_SPEED_FREQ_HIGH;
        case GPIO_SPEED_VERY_HIGH:  return GPIO_SPEED_FREQ_VERY_HIGH;
        default:                    return GPIO_SPEED_FREQ_LOW;
    }
}

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       GPIO引脚初始化
 *
 * @param[in]   GPIOx GPIO端口
 * @param[in]   GPIO_Pin GPIO引脚号
 * @param[in]   mode GPIO模式
 * @param[in]   pull 上下拉配置
 * @param[in]   speed GPIO速度
 *
 * @retval      无
 *
 * @note        自动使能时钟并初始化GPIO
 */
void gpio_init_pin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_Mode_e mode, GPIO_Pull_e pull, GPIO_Speed_e speed)
{
    GPIO_InitTypeDef gpio_init_struct = {0};

    /* 使能时钟 */
    gpio_enable_clock(GPIOx);

    /* 配置GPIO */
    gpio_init_struct.Pin = GPIO_Pin;
    gpio_init_struct.Mode = gpio_get_hal_mode(mode);
    gpio_init_struct.Pull = gpio_get_hal_pull(pull);
    gpio_init_struct.Speed = gpio_get_hal_speed(speed);

    /* 初始化 */
    HAL_GPIO_Init(GPIOx, &gpio_init_struct);

    log_d("GPIO初始化完成: Port=0x%08X, Pin=0x%04X", (uint32_t)GPIOx, GPIO_Pin);
}

/**
 * @brief       写GPIO引脚电平
 *
 * @param[in]   GPIOx GPIO端口
 * @param[in]   GPIO_Pin GPIO引脚号
 * @param[in]   level 电平(GPIO_PIN_LOW/GPIO_PIN_HIGH)
 *
 * @retval      无
 */
void gpio_write_pin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinLevel_e level)
{
    HAL_GPIO_WritePin(GPIOx, GPIO_Pin, (GPIO_PinState)level);
}

/**
 * @brief       读GPIO引脚电平
 *
 * @param[in]   GPIOx GPIO端口
 * @param[in]   GPIO_Pin GPIO引脚号
 *
 * @retval      电平(GPIO_PIN_LOW/GPIO_PIN_HIGH)
 */
GPIO_PinLevel_e gpio_read_pin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
    return (GPIO_PinLevel_e)HAL_GPIO_ReadPin(GPIOx, GPIO_Pin);
}

/**
 * @brief       翻转GPIO引脚电平
 *
 * @param[in]   GPIOx GPIO端口
 * @param[in]   GPIO_Pin GPIO引脚号
 *
 * @retval      无
 */
void gpio_toggle_pin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
    HAL_GPIO_TogglePin(GPIOx, GPIO_Pin);
}

/**
 * @brief       写GPIO端口值
 *
 * @param[in]   GPIOx GPIO端口
 * @param[in]   value 16位端口值
 *
 * @retval      无
 */
void gpio_write_port(GPIO_TypeDef *GPIOx, uint16_t value)
{
    GPIOx->ODR = value;
}

/**
 * @brief       读GPIO端口值
 *
 * @param[in]   GPIOx GPIO端口
 *
 * @retval      16位端口值
 */
uint16_t gpio_read_port(GPIO_TypeDef *GPIOx)
{
    return (uint16_t)(GPIOx->IDR);
}

/**
 * @brief       锁定GPIO引脚配置
 *
 * @param[in]   GPIOx GPIO端口
 * @param[in]   GPIO_Pin GPIO引脚号
 *
 * @retval      无
 *
 * @note        锁定后引脚配置无法修改，直到下次复位
 */
void gpio_lock_pin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
    HAL_GPIO_LockPin(GPIOx, GPIO_Pin);
    log_i("GPIO引脚已锁定: Port=0x%08X, Pin=0x%04X", (uint32_t)GPIOx, GPIO_Pin);
}

